import {
  Fog,
  Group,
  MeshBasicMaterial,
  DirectionalLight,
  DirectionalLightHelper,
  AmbientLight,
  PointLight,
  Vector3,
  PointLightHelper,
  MeshLambertMaterial,
  LineBasicMaterial,
  Color,
  Vector2,
  MeshStandardMaterial,
  BufferGeometry,
  PlaneGeometry,
  Mesh,
  DoubleSide,
  RepeatWrapping,
  SRGBColorSpace,
  LinearSRGBColorSpace,
  AdditiveBlending,
  VideoTexture,
  NearestFilter,
  BoxGeometry,
  TubeGeometry,
  QuadraticBezierCurve3,
  PointsMaterial,
  Sprite,
  SpriteMaterial,
} from "three";
import {
  Mini3d,
  Grid,
  Label3d,
  Plane,
  GradientShader,
  Particles,
} from "../../mini3d";
import { Assets } from "./map-zj/assets";
import { ExtrudeMap } from "./map-zj/extrudeMap";
import { BaseMap } from "./map-zj/baseMap";
import { Line } from "./map-zj/line";
import { geoMercator } from "d3-geo";
import labelIcon from "@/assets/texture/label-icon.png";
import chinaData from "./map-zj/chinaData";
import provincesData from "./map-zj/provincesData";
import scatterData from "./map-zj/scatter";
import infoData from "./map-zj/infoData";
import gsap from "gsap";
import { InteractionManager } from "three.interactive";
// 排序
function sortByValue(data) {
  data.sort((a, b) => b.value - a.value);
  return data;
}
export class World extends Mini3d {
  constructor(canvas) {
    super(canvas);
    // 中心坐标
    this.pointCenter = [120.109913, 29.181466];
    this.flyLineCenter = [119.476498, 29.898918];
    this.depth = 0.5;
    this.clicked = false;
    this.scene.fog = new Fog(0x102736, 1, 50);
    this.scene.background = new Color(0x102736);
    // 相机初始位置
    this.camera.instance.position.set(
      -13.767695123014105,
      12.990152163077308,
      39.28228164159694
    );
    this.camera.instance.near = 1;
    this.camera.instance.far = 10000;
    this.camera.instance.updateProjectionMatrix();
    // 创建交互管理
    this.interactionManager = new InteractionManager(
      this.renderer.instance,
      this.camera.instance,
      this.canvas
    );
    // 播放状态
    this.playing = false;
    // 创建环境光
    this.initEnvironment();
    // 资源加载完成后执行
    this.assets = new Assets(() => {
      this.labelGroup = new Group();
      console.log("this", this);
      this.label3d = new Label3d(this);
      this.labelGroup.rotateX(-Math.PI / 2);
      // 事件元素
      this.eventElement = [];
      // 鼠标移上移除的材质
      this.defaultMaterial = null; // 默认材质
      this.defaultLightMaterial = null; // 高亮材质
      this.scene.add(this.labelGroup);
      // 创建底部高亮
      this.createFloor();
      // 模糊边线
      this.createChinaBlurLine();
      // 扩散网格
      this.createGrid();
      // 旋转圆环
      this.createRotateBorder();
      // 创建标签
      this.createLabel();
      // 创建地图
      this.createMap();
      // 创建视频动画
      // this.createAnimateVideo();
      this.createEvent();
      // 创建飞线
      this.createFlyLine();
      // 创建粒子
      this.createParticles();
      // 创建散点图
      this.createScatter();
      // 创建信息点
      this.createInfoPoint();
      // 创建动画时间线
      let tl = gsap.timeline();
      // 相机动画
      tl.addLabel("focusMap", 2);
      tl.addLabel("focusMapOpacity", 2.5);
      tl.addLabel("bar", 3.5);
      tl.add(
        gsap.to(this.camera.instance.position, {
          duration: 2.5,
          x: -0.2515849818960619,
          y: 12.397744557047988,
          z: 14.647659671139275,
          ease: "circ.out",
        })
      );
      // 广东出现
      tl.add(
        gsap.to(this.focusMapGroup.position, {
          duration: 1,
          x: 0,
          y: 0,
          z: 0,
        }),
        "focusMap"
      );
      tl.add(
        gsap.to(this.focusMapGroup.scale, {
          duration: 1,
          x: 1,
          y: 1,
          z: 1,
          ease: "circ.out",
        }),
        "focusMap"
      );

      tl.add(
        gsap.to(this.focusMapTopMaterial, {
          duration: 1,
          opacity: 1,
          ease: "circ.out",
        }),
        "focusMapOpacity"
      );
      tl.add(
        gsap.to(this.focusMapSideMaterial, {
          duration: 1,
          opacity: 1,
          ease: "circ.out",
          onComplete: () => {
            this.focusMapSideMaterial.transparent = false;
          },
        }),
        "focusMapOpacity"
      );
      // this.otherLabel.map((item, index) => {
      //   let element = item.element.querySelector(".other-label");
      //   tl.add(
      //     gsap.to(element, {
      //       duration: 1,
      //       delay: 0.1 * index,
      //       translateY: 0,
      //       opacity: 1,
      //       ease: "circ.out",
      //     }),
      //     "focusMapOpacity"
      //   );
      // });
      tl.add(
        gsap.to(this.zhejiangLineMaterial, {
          duration: 0.5,
          delay: 0.3,
          opacity: 1,
        }),
        "focusMapOpacity"
      );
      tl.add(
        gsap.to(this.rotateBorder1.scale, {
          delay: 0.3,
          duration: 1,
          x: 1,
          y: 1,
          z: 1,
          ease: "circ.out",
        }),
        "focusMapOpacity"
      );
      tl.add(
        gsap.to(this.rotateBorder2.scale, {
          duration: 1,
          delay: 0.5,
          x: 1,
          y: 1,
          z: 1,
          ease: "circ.out",
        }),
        "focusMapOpacity"
      );
      this.allBar.map((item, index) => {
        tl.add(
          gsap.to(item.scale, {
            duration: 1,
            delay: 0.1 * index,
            x: 1,
            y: 1,
            z: 1,
            ease: "circ.out",
          }),
          "bar"
        );
      });
      this.allBarMaterial.map((item, index) => {
        tl.add(
          gsap.to(item, {
            duration: 1,
            delay: 0.1 * index,
            opacity: 1,
            ease: "circ.out",
          }),
          "bar"
        );
      });

      this.allProvinceLabel.map((item, index) => {
        let element = item.element.querySelector(".provinces-label-wrap");
        let number = item.element.querySelector(".number .value");
        let numberVal = Number(number.innerText);
        let numberAnimate = {
          score: 0,
        };
        tl.add(
          gsap.to(element, {
            duration: 1,
            delay: 0.2 * index,
            translateY: 0,
            opacity: 1,
            ease: "circ.out",
          }),
          "bar"
        );
        let text = gsap.to(numberAnimate, {
          duration: 1,
          delay: 0.2 * index,
          score: numberVal,
          onUpdate: showScore,
        });
        function showScore() {
          number.innerText = numberAnimate.score.toFixed(0);
        }
        tl.add(text, "bar");
      });
      this.allGuangquan.map((item, index) => {
        tl.add(
          gsap.to(item.children[0].scale, {
            duration: 1,
            delay: 0.1 * index,
            x: 1,
            y: 1,
            z: 1,
            ease: "circ.out",
          }),
          "bar"
        );
        tl.add(
          gsap.to(item.children[1].scale, {
            duration: 1,
            delay: 0.1 * index,
            x: 1,
            y: 1,
            z: 1,
            ease: "circ.out",
          }),
          "bar"
        );
      });
    });
  }
  // 初始化环境灯光
  initEnvironment() {
    let sun = new AmbientLight(0xffffff, 5);
    this.scene.add(sun);
    // 方向光
    let directionalLight = new DirectionalLight(0xffffff, 5);
    directionalLight.position.set(-30, 6, -8);
    directionalLight.castShadow = true;
    directionalLight.shadow.radius = 20;
    directionalLight.shadow.mapSize.width = 1024;
    directionalLight.shadow.mapSize.height = 1024;
    this.scene.add(directionalLight);

    this.createPointLight({
      color: "#1d5e5e",
      // intensity: 27,
      intensity: 600,
      distance: 10000,
      x: -9,
      y: 3,
      z: -3,
    });
    this.createPointLight({
      color: "#1d5e5e",
      intensity: 100,
      distance: 10000,
      x: 0,
      y: 2,
      z: 5,
    });
  }

  createPointLight(pointParams) {
    // 点光源
    const pointLight = new PointLight(
      0x1d5e5e,
      pointParams.intensity,
      pointParams.distance
    );
    pointLight.position.set(pointParams.x, pointParams.y, pointParams.z);
    this.scene.add(pointLight);
  }

  // 创建地图
  createMap() {
    let mapGroup = new Group();
    let focusMapGroup = new Group();

    this.focusMapGroup = focusMapGroup;
    let { china, chinaTopLine } = this.createChina();
    let { zhejiang, zhejiangTop, guangdonLine } = this.createProvince();
    china.setParent(mapGroup);
    chinaTopLine.setParent(mapGroup);

    zhejiang.setParent(focusMapGroup);
    zhejiangTop.setParent(focusMapGroup);
    guangdonLine.setParent(focusMapGroup);
    focusMapGroup.position.set(0, 0, -0.01);
    focusMapGroup.scale.set(1, 1, 0);
    mapGroup.add(focusMapGroup);
    mapGroup.rotateX(-Math.PI / 2);
    mapGroup.position.set(0, 0.2, 0);

    this.scene.add(mapGroup);
    this.createBar();
  }
  // 创建中国
  createChina() {
    // 中国地图
    let chinaData = this.assets.instance.getResource("china");
    let china = new BaseMap(this, {
      data: chinaData,
      center: this.pointCenter,
      merge: false,
      material: new MeshLambertMaterial({
        color: 0x152c47,
        transparent: true,
        opacity: 1,
      }),

      renderOrder: 2,
    });

    let chinaTopLine = new Line(this, {
      center: this.pointCenter,
      visibelProvince: "浙江省",
      data: chinaData,
      material: new LineBasicMaterial({ color: 0x3f82cd }),
      renderOrder: 3,
    });

    chinaTopLine.lineGroup.position.z += 0.01;

    let chinaBottomLine = new Line(this, {
      center: this.pointCenter,
      data: chinaData,
      material: new LineBasicMaterial({
        color: 0x3f82cd,
        transparent: true,
        opacity: 0.4,
      }),
      renderOrder: 3,
    });

    chinaBottomLine.lineGroup.position.z -= 0.59;
    return { china, chinaTopLine, chinaBottomLine };
  }
  // 创建省份
  createProvince() {
    // 广东地图
    let zhejiangData = this.assets.instance.getResource("zhejiang");
    let [topMaterial, sideMaterial] = this.createProvinceMaterial();
    this.focusMapTopMaterial = topMaterial;
    this.focusMapSideMaterial = sideMaterial;
    let zhejiang = new ExtrudeMap(this, {
      center: this.pointCenter,
      position: new Vector3(0, 0, 0.11),
      data: zhejiangData,
      depth: 0.5,
      topFaceMaterial: topMaterial,
      sideMaterial: sideMaterial,
      renderOrder: 9,
    });

    let faceMaterial = new MeshStandardMaterial({
      color: 0xffffff,
      transparent: true,
      opacity: 0.5,
    });
    new GradientShader(faceMaterial, { uColor1: 0x2a6e92, uColor2: 0x102736 });
    // 设置默认材质
    this.defaultMaterial = faceMaterial;
    this.defaultLightMaterial = this.defaultMaterial.clone();
    this.defaultLightMaterial.emissive.setHex(0x0b112d);
    this.defaultLightMaterial.emissiveIntensity = 3.5;
    let zhejiangTop = new BaseMap(this, {
      center: this.pointCenter,
      position: new Vector3(0, 0, 0.72),
      data: zhejiangData,
      material: faceMaterial,

      renderOrder: 2,
    });
    zhejiangTop.mapGroup.children.map((group) => {
      group.children.map((mesh) => {
        if (mesh.type === "Mesh") {
          this.eventElement.push(mesh);
        }
      });
    });
    // 地图线
    this.zhejiangLineMaterial = new LineBasicMaterial({
      color: 0xffffff,
      opacity: 0,
      transparent: true,
      fog: false,
    });
    let guangdonLine = new Line(this, {
      center: this.pointCenter,
      data: zhejiangData,
      material: this.zhejiangLineMaterial,
      renderOrder: 3,
    });

    guangdonLine.lineGroup.position.z += 0.73;

    return {
      zhejiang,
      zhejiangTop,
      guangdonLine,
    };
  }
  createProvinceMaterial() {
    let topMaterial = new MeshLambertMaterial({
      color: 0xffffff,
      transparent: true,
      opacity: 0,
      fog: false,
      side: DoubleSide,
    });
    topMaterial.onBeforeCompile = (shader) => {
      shader.uniforms = {
        ...shader.uniforms,
        uColor1: { value: new Color(0x2a6e92) }, // 419daa
        uColor2: { value: new Color(0x102736) },
      };
      shader.vertexShader = shader.vertexShader.replace(
        "void main() {",
        `
        attribute float alpha;
        varying vec3 vPosition;
        varying float vAlpha;
        void main() {
          vAlpha = alpha;
          vPosition = position;
      `
      );
      shader.fragmentShader = shader.fragmentShader.replace(
        "void main() {",
        `
        varying vec3 vPosition;
        varying float vAlpha;
        uniform vec3 uColor1;
        uniform vec3 uColor2;
      
        void main() {
      `
      );
      shader.fragmentShader = shader.fragmentShader.replace(
        "#include <opaque_fragment>",
        /* glsl */ `
      #ifdef OPAQUE
      diffuseColor.a = 1.0;
      #endif
      
      // https://github.com/mrdoob/three.js/pull/22425
      #ifdef USE_TRANSMISSION
      diffuseColor.a *= transmissionAlpha + 0.1;
      #endif
      vec3 gradient = mix(uColor1, uColor2, vPosition.x/15.78); // 15.78
      
      outgoingLight = outgoingLight*gradient;
     
      if(vPosition.z>0.3){
        diffuseColor.a *= 0.5;
      }
      
      gl_FragColor = vec4( outgoingLight, diffuseColor.a  );
      `
      );
    };
    let sideMap = this.assets.instance.getResource("side");
    sideMap.wrapS = RepeatWrapping;
    sideMap.wrapT = RepeatWrapping;
    sideMap.repeat.set(1, 1.5);
    sideMap.offset.y += 0.065;
    let sideMaterial = new MeshStandardMaterial({
      color: 0xffffff,
      map: sideMap,
      fog: false,
      side: DoubleSide,
    });

    this.time.on("tick", () => {
      sideMap.offset.y += 0.005;
    });
    sideMaterial.onBeforeCompile = (shader) => {
      shader.uniforms = {
        ...shader.uniforms,
        uColor1: { value: new Color(0x2a6e92) },
        uColor2: { value: new Color(0x2a6e92) },
      };
      shader.vertexShader = shader.vertexShader.replace(
        "void main() {",
        `
        attribute float alpha;
        varying vec3 vPosition;
        varying float vAlpha;
        void main() {
          vAlpha = alpha;
          vPosition = position;
      `
      );
      shader.fragmentShader = shader.fragmentShader.replace(
        "void main() {",
        `
        varying vec3 vPosition;
        varying float vAlpha;
        uniform vec3 uColor1;
        uniform vec3 uColor2;
      
        void main() {
      `
      );
      shader.fragmentShader = shader.fragmentShader.replace(
        "#include <opaque_fragment>",
        /* glsl */ `
      #ifdef OPAQUE
      diffuseColor.a = 1.0;
      #endif
      
      // https://github.com/mrdoob/three.js/pull/22425
      #ifdef USE_TRANSMISSION
      diffuseColor.a *= transmissionAlpha + 0.1;
      #endif
      vec3 gradient = mix(uColor1, uColor2, vPosition.z/1.2);
      
      outgoingLight = outgoingLight*gradient;
      
      
      gl_FragColor = vec4( outgoingLight, diffuseColor.a  );
      `
      );
    };
    return [topMaterial, sideMaterial];
  }
  // 创建柱状图
  createBar() {
    let self = this;
    let data = sortByValue(provincesData).filter((item, index) => index < 7);

    const barGroup = new Group();
    // 最大高度
    const factor = 0.7;
    const height = 4.0 * factor;
    const max = data[0].value;
    // 存储动画元素
    this.allBar = [];
    this.allBarMaterial = [];
    this.allGuangquan = [];
    this.allProvinceLabel = [];
    data.map((item, index) => {
      // 网格
      let geoHeight = height * (item.value / max);
      // 材质
      let material = new MeshBasicMaterial({
        color: 0xffffff,
        transparent: true,
        opacity: 0,
        depthTest: false,
        fog: false,
      });
      // 设置渐变材质
      // FBDF88 FFFEF4
      new GradientShader(material, {
        uColor1: index > 3 ? 0xfbdf88 : 0x50bbfe,
        uColor2: index > 3 ? 0xfffef4 : 0x77fbf5,
        size: geoHeight,
        dir: "y",
      });
      const geo = new BoxGeometry(0.1 * factor, 0.1 * factor, geoHeight);
      // 上移
      geo.translate(0, 0, geoHeight / 2);
      const mesh = new Mesh(geo, material);
      mesh.renderOrder = 5;
      let areaBar = mesh;
      let [x, y] = this.geoProjection(item.centroid);
      areaBar.position.set(x, -y, 0.95);
      areaBar.scale.set(1, 1, 0);
      let guangQuan = this.createQuan(new Vector3(x, 0.94, y), index);
      let hg = this.createHUIGUANG(geoHeight, index > 3 ? 0xfffef4 : 0x77fbf5);
      areaBar.add(...hg);
      barGroup.add(areaBar);
      barGroup.rotation.x = -Math.PI / 2;
      let barLabel = labelStyle04(
        item,
        index,
        new Vector3(x, -y, 1.6 + geoHeight)
      );
      this.allBar.push(areaBar);
      this.allBarMaterial.push(material);
      this.allGuangquan.push(guangQuan);
      this.allProvinceLabel.push(barLabel);
    });
    this.scene.add(barGroup);

    function labelStyle04(data, index, position) {
      let label = self.label3d.create("", "provinces-label", false);
      label.init(
        `<div class="provinces-label ${index > 4 ? "yellow" : ""}">
      <div class="provinces-label-wrap">
        <div class="number"><span class="value">${
          data.value
        }</span><span class="unit">万人</span></div>
        <div class="name">
          <span class="zh">${data.name}</span>
          <span class="en">${data.enName.toUpperCase()}</span>
        </div>
        <div class="no">${index + 1}</div>
      </div>
    </div>`,
        position
      );
      self.label3d.setLabelStyle(label, 0.01, "x");

      label.setParent(self.labelGroup);
      return label;
    }
  }
  // 添加事件
  createEvent() {
    // hover的对象
    let objectsHover = [];
    // 重置
    const reset = (mesh) => {
      // 还原颜色
      mesh.traverse((obj) => {
        if (obj.isMesh) {
          obj.material = this.defaultMaterial;
        }
      });
    };
    const move = (mesh) => {
      // 设置发光颜色
      mesh.traverse((obj) => {
        if (obj.isMesh) {
          obj.material = this.defaultLightMaterial;
        }
      });
    };
    //
    // 循环添加事件
    this.eventElement.map((mesh) => {
      this.interactionManager.add(mesh);
      mesh.addEventListener("mousedown", (ev) => {
        console.log(ev.target.userData.name);
      });

      mesh.addEventListener("mouseover", (event) => {
        if (!objectsHover.includes(event.target.parent)) {
          objectsHover.push(event.target.parent);
        }
        document.body.style.cursor = "pointer";
        move(event.target.parent);
      });
      mesh.addEventListener("mouseout", (event) => {
        objectsHover = objectsHover.filter(
          (n) => n.userData.name !== event.target.parent.userData.name
        );
        if (objectsHover.length > 0) {
          const mesh = objectsHover[objectsHover.length - 1];
        }
        reset(event.target.parent);
        document.body.style.cursor = "default";
      });
    });
  }
  createHUIGUANG(h, color) {
    let geometry = new PlaneGeometry(0.35, h);
    geometry.translate(0, h / 2, 0);
    const texture = this.assets.instance.getResource("huiguang");
    texture.colorSpace = SRGBColorSpace;
    texture.wrapS = RepeatWrapping;
    texture.wrapT = RepeatWrapping;
    let material = new MeshBasicMaterial({
      color: color,
      map: texture,
      transparent: true,
      opacity: 0.4,
      depthWrite: false,
      side: DoubleSide,
      blending: AdditiveBlending,
    });
    let mesh = new Mesh(geometry, material);
    mesh.renderOrder = 10;
    mesh.rotateX(Math.PI / 2);
    let mesh2 = mesh.clone();
    let mesh3 = mesh.clone();
    mesh2.rotateY((Math.PI / 180) * 60);
    mesh3.rotateY((Math.PI / 180) * 120);
    return [mesh, mesh2, mesh3];
  }
  createQuan(position, index) {
    const guangquan1 = this.assets.instance.getResource("guangquan1");
    const guangquan2 = this.assets.instance.getResource("guangquan2");
    let geometry = new PlaneGeometry(0.5, 0.5);

    let material1 = new MeshBasicMaterial({
      color: 0xffffff,
      map: guangquan1,
      alphaMap: guangquan1,
      opacity: 1,
      transparent: true,
      depthTest: false,
      fog: false,
      blending: AdditiveBlending,
    });
    let material2 = new MeshBasicMaterial({
      color: 0xffffff,
      map: guangquan2,
      alphaMap: guangquan2,
      opacity: 1,
      transparent: true,
      depthTest: false,
      fog: false,
      blending: AdditiveBlending,
    });
    let mesh1 = new Mesh(geometry, material1);
    let mesh2 = new Mesh(geometry, material2);
    mesh1.renderOrder = 6;
    mesh2.renderOrder = 6;
    mesh1.rotateX(-Math.PI / 2);
    mesh2.rotateX(-Math.PI / 2);
    mesh1.position.copy(position);
    mesh2.position.copy(position);
    mesh2.position.y -= 0.001;
    mesh1.scale.set(0, 0, 0);
    mesh2.scale.set(0, 0, 0);
    this.quanGroup = new Group();
    this.quanGroup.add(mesh1, mesh2);
    this.scene.add(this.quanGroup);
    this.time.on("tick", () => {
      mesh1.rotation.z += 0.05;
    });
    return this.quanGroup;
  }
  /**
   * 创建网格
   */
  createGrid() {
    new Grid(this, {
      gridSize: 50,
      gridDivision: 20,
      gridColor: 0x1b4b70,
      shapeSize: 0.5,
      shapeColor: 0x2a5f8a,
      pointSize: 0.1,
      pointColor: 0x154d7d,
      diffuse: true,
      diffuseSpeed: 10.0,
      diffuseColor: 0x2e8bd9,
    });
  }
  /**
   * 创建地板
   */
  createFloor() {
    let geometry = new PlaneGeometry(20, 20);
    const texture = this.assets.instance.getResource("ocean");
    texture.colorSpace = SRGBColorSpace;
    texture.wrapS = RepeatWrapping;
    texture.wrapT = RepeatWrapping;
    texture.repeat.set(1, 1);
    let material = new MeshBasicMaterial({
      map: texture,
      opacity: 1,
    });
    let mesh = new Mesh(geometry, material);
    mesh.rotateX(-Math.PI / 2);
    mesh.position.set(0, -0.7, 0);
    this.scene.add(mesh);
  }
  /**
   * 创建中国模糊边界
   */
  createChinaBlurLine() {
    let geometry = new PlaneGeometry(147, 147);
    const texture = this.assets.instance.getResource("chinaBlurLine");
    texture.colorSpace = SRGBColorSpace;
    texture.wrapS = RepeatWrapping;
    texture.wrapT = RepeatWrapping;
    texture.generateMipmaps = false;
    texture.minFilter = NearestFilter;
    texture.repeat.set(1, 1);
    let material = new MeshBasicMaterial({
      color: 0x3f82cd,
      alphaMap: texture,
      transparent: true,
      opacity: 0.5,
    });
    let mesh = new Mesh(geometry, material);
    mesh.rotateX(-Math.PI / 2);
    mesh.position.set(-33.2, -0.5, -5.2);
    this.scene.add(mesh);
  }
  /**
   * 创建视频动画
   */
  createAnimateVideo() {
    this.createAnimateVideoItem(".map-zj-video1", new Vector3(11, 0.4, 1));
    this.createAnimateVideoItem(".map-zj-video2", new Vector3(-11, 0.4, 2));
  }
  createAnimateVideoItem(className, position) {
    let video1 = document.querySelector(className);
    window.addEventListener("pointerdown", () => {
      if (!this.playing) {
        video1.play();
        this.playing = true;
      }
    });
    let texture = new VideoTexture(video1);
    texture.colorSpace = SRGBColorSpace;
    let scale = 1.2;
    let geometry = new PlaneGeometry(2.5 * scale, 1 * scale);
    let material = new MeshBasicMaterial({
      color: 0xa4e7f6,
      alphaMap: texture,
      transparent: true,
      opacity: 1,
      blending: AdditiveBlending,
    });
    let mesh = new Mesh(geometry, material);
    mesh.rotateX(-Math.PI / 2);
    mesh.position.copy(position);
    mesh.renderOrder = 10;
    this.scene.add(mesh);
  }

  createLabel() {
    let self = this;
    let labelGroup = this.labelGroup;
    let label3d = this.label3d;
    let otherLabel = [];
    chinaData.map((province) => {
      if (province.hide == true) return false;
      let label = labelStyle01(province, label3d, labelGroup);
      otherLabel.push(label);
    });

    let gdLabel = labelStyle02(
      {
        name: "浙江省",
        enName: "ZHEJIANG PROVINCE",
        center: [120.109913, 26.881466],
      },
      label3d,
      labelGroup
    );

    let iconLabel1 = labelStyle03(
      {
        icon: labelIcon,
        center: [125.109913, 26.881466],
        width: "40px",
        height: "40px",
        reflect: true,
      },
      label3d,
      labelGroup
    );

    let iconLabel2 = labelStyle03(
      {
        icon: labelIcon,
        center: [116.109913, 26.881466],
        width: "20px",
        height: "20px",
        reflect: false,
      },
      label3d,
      labelGroup
    );
    // otherLabel.push(gdLabel);
    // otherLabel.push(iconLabel1);
    // otherLabel.push(iconLabel2);
    // this.otherLabel = otherLabel;
    // 省份名字标签
    function labelStyle01(province, label3d, labelGroup) {
      let label = label3d.create(
        "",
        `china-label ${province.blur ? " blur" : ""}`,
        false
      );
      const [x, y] = self.geoProjection(province.center);
      label.init(
        `<div class="other-label"><img class="label-icon" src="${labelIcon}">${province.name}</div>`,
        new Vector3(x, -y, 0.4)
      );
      label3d.setLabelStyle(label, 0.02, "x");
      label.setParent(labelGroup);
      return label;
    }
    // 浙江省标签
    function labelStyle02(province, label3d, labelGroup) {
      let label = label3d.create("", "guangdong-label", false);
      const [x, y] = self.geoProjection(province.center);
      label.init(
        `<div class="other-label"><span>${province.name}</span><span>${province.enName}</span></div>`,
        new Vector3(x, -y, 0.4)
      );
      label3d.setLabelStyle(label, 0.02, "x");
      label.setParent(labelGroup);
      return label;
    }
    // 辅助元素标签
    function labelStyle03(data, label3d, labelGroup) {
      let label = label3d.create(
        "",
        `decoration-label  ${data.reflect ? " reflect" : ""}`,
        false
      );
      const [x, y] = self.geoProjection(data.center);
      label.init(
        `<div class="other-label"><img class="label-icon" style="width:${data.width};height:${data.height}" src="${data.icon}">`,
        new Vector3(x, -y, 0.4)
      );
      label3d.setLabelStyle(label, 0.02, "x");
      label.setParent(labelGroup);
      return label;
    }
  }
  createRotateBorder() {
    //
    let max = 12;
    let rotationBorder1 = this.assets.instance.getResource("rotationBorder1");
    let rotationBorder2 = this.assets.instance.getResource("rotationBorder2");
    let plane01 = new Plane(this, {
      width: max * 1.178,
      needRotate: true,
      rotateSpeed: 0.001,
      material: new MeshBasicMaterial({
        map: rotationBorder1,
        color: 0x48afff,
        transparent: true,
        opacity: 0.2,
        side: DoubleSide,
        depthWrite: false,
        blending: AdditiveBlending,
      }),
      position: new Vector3(0, 0.28, 0),
    });
    plane01.instance.renderOrder = 6;
    plane01.instance.scale.set(0, 0, 0);
    plane01.setParent(this.scene);
    //
    let plane02 = new Plane(this, {
      width: max * 1.116,
      needRotate: true,
      rotateSpeed: -0.004,
      material: new MeshBasicMaterial({
        map: rotationBorder2,
        color: 0x48afff,
        transparent: true,
        opacity: 0.4,
        side: DoubleSide,
        depthWrite: false,
        blending: AdditiveBlending,
      }),
      position: new Vector3(0, 0.3, 0),
    });
    plane02.instance.renderOrder = 6;
    plane02.instance.scale.set(0, 0, 0);
    plane02.setParent(this.scene);
    this.rotateBorder1 = plane01.instance;
    this.rotateBorder2 = plane02.instance;
  }

  // 创建飞线
  createFlyLine() {
    this.flyLineGroup = new Group();
    this.flyLineGroup.visible = false;
    this.scene.add(this.flyLineGroup);
    // 贴图
    const texture = this.assets.instance.getResource("flyLine");
    texture.colorSpace = SRGBColorSpace;
    texture.wrapS = RepeatWrapping;
    texture.wrapT = RepeatWrapping;
    // texture.generateMipmaps = false
    // texture.magFilter = NearestFilter
    texture.repeat.set(1, 1);
    const tubeRadius = 0.03;
    const tubeSegments = 32;
    const tubeRadialSegments = 8;
    const closed = false;
    let [centerX, centerY] = this.geoProjection(this.flyLineCenter);
    let centerPoint = new Vector3(centerX, -centerY, 0);
    const material = new MeshBasicMaterial({
      map: texture,
      alphaMap: texture,
      // 0x2a6f72  0x0d2025
      color: 0x2a6f72,
      transparent: true,
      fog: false,
      opacity: 1,
      depthTest: false,
      blending: AdditiveBlending,
    });
    this.time.on("tick", () => {
      texture.offset.x -= 0.006;
    });
    provincesData
      .filter((item, index) => index < 7)
      .map((city) => {
        let [x, y] = this.geoProjection(city.centroid);
        let point = new Vector3(x, -y, 0);
        const center = new Vector3();
        center.addVectors(centerPoint, point).multiplyScalar(0.5);
        // 设置曲线中间点的高度
        center.setZ(3);
        // 三维二次贝塞尔曲线
        const curve = new QuadraticBezierCurve3(centerPoint, center, point);
        // 管道几何体
        const tubeGeometry = new TubeGeometry(
          curve,
          tubeSegments,
          tubeRadius,
          tubeRadialSegments,
          closed
        );
        const mesh = new Mesh(tubeGeometry, material);
        mesh.rotation.x = -Math.PI / 2;
        mesh.position.set(0, 0.94, 0);
        mesh.renderOrder = 21;
        this.flyLineGroup.add(mesh);
      });
    this.createFlyLineFocus();
  }
  createFlyLineFocus() {
    this.flyLineFocusGroup = new Group();
    this.flyLineFocusGroup.visible = false;
    this.flyLineFocusGroup.rotation.x = -Math.PI / 2;
    let [x, y] = this.geoProjection([119.476498, 29.898918]);
    this.flyLineFocusGroup.position.set(x, 0.942, y);
    this.scene.add(this.flyLineFocusGroup);
    const flyLineFocus = this.assets.instance.getResource("flyLineFocus");
    const geometry = new PlaneGeometry(1, 1);
    const material = new MeshBasicMaterial({
      color: 0xffffff,
      map: flyLineFocus,
      alphaMap: flyLineFocus,
      transparent: true,
      fog: false,
      depthTest: false,
      blending: AdditiveBlending,
    });
    const mesh = new Mesh(geometry, material);

    mesh.scale.set(0, 0, 0);
    const mesh2 = mesh.clone();
    mesh2.material = material.clone();
    this.flyLineFocusGroup.add(mesh, mesh2);
    gsap.to(mesh.material, {
      opacity: 0,
      repeat: -1,
      yoyo: false,
      duration: 1,
    });
    gsap.to(mesh.scale, {
      x: 1.5,
      y: 1.5,
      z: 1.5,
      repeat: -1,
      yoyo: false,
      duration: 1,
    });
    gsap.to(mesh2.material, {
      delay: 0.5,
      opacity: 0,
      repeat: -1,
      yoyo: false,
      duration: 1,
    });
    gsap.to(mesh2.scale, {
      delay: 0.5,
      x: 1.5,
      y: 1.5,
      z: 1.5,
      repeat: -1,
      yoyo: false,
      duration: 1,
    });
  }
  // 创建粒子
  createParticles() {
    this.particles = new Particles(this, {
      num: 10, // 粒子数量
      range: 30, // 范围
      dir: "up",
      speed: 0.05,
      material: new PointsMaterial({
        map: Particles.createTexture(),
        size: 1,
        color: 0x00eeee,
        transparent: true,
        opacity: 1,
        depthTest: false,
        depthWrite: false,
        vertexColors: true,
        blending: AdditiveBlending,
        sizeAttenuation: true,
      }),
    });
    this.particles.instance.position.set(0, 0, 0);
    this.particles.instance.rotation.x = -Math.PI / 2;
    this.particles.setParent(this.scene);
    // 停用,隐藏
    this.particles.enable = false;
    this.particles.instance.visible = false;
  }
  // 创建散点图
  createScatter() {
    this.scatterGroup = new Group();
    this.scatterGroup.visible = false;
    this.scatterGroup.rotation.x = -Math.PI / 2;
    this.scene.add(this.scatterGroup);
    // 贴图
    const texture = this.assets.instance.getResource("arrow");
    const material = new SpriteMaterial({
      map: texture,
      color: 0xfffef4,
      fog: false,
      transparent: true,
      depthTest: false,
    });

    let scatterAllData = sortByValue(scatterData);
    let max = scatterAllData[0].value;
    scatterAllData.map((data) => {
      const sprite = new Sprite(material);
      sprite.renderOrder = 23;
      let scale = 0.1 + (data.value / max) * 0.2;
      sprite.scale.set(scale, scale, scale);
      let [x, y] = this.geoProjection([data.lng, data.lat]);
      sprite.position.set(x, -y, this.depth + 0.45);
      sprite.userData.position = [x, -y, this.depth + 0.45];
      this.scatterGroup.add(sprite);
    });
  }
  // 创建信息点
  createInfoPoint() {
    let self = this;
    this.InfoPointGroup = new Group();
    this.InfoPointGroup.visible = false;
    this.InfoPointGroup.rotation.x = -Math.PI / 2;
    this.scene.add(this.InfoPointGroup);
    this.infoPointIndex = 0;
    this.infoPointLabelTime = null;
    this.infoLabelElement = [];
    let label3d = this.label3d;
    // 贴图
    const texture = this.assets.instance.getResource("point");
    let colors = [0xfffef4, 0x77fbf5];

    let infoAllData = sortByValue(infoData);
    let max = infoAllData[0].value;
    infoAllData.map((data, index) => {
      const material = new SpriteMaterial({
        map: texture,
        color: colors[index % colors.length],
        fog: false,
        transparent: true,
        depthTest: false,
      });
      const sprite = new Sprite(material);

      sprite.renderOrder = 23;
      let scale = 0.7 + (data.value / max) * 0.4;
      sprite.scale.set(scale, scale, scale);
      let [x, y] = this.geoProjection([data.lng, data.lat]);
      let position = [x, -y, this.depth + 0.7];
      sprite.position.set(...position);
      sprite.userData.position = [...position];
      sprite.userData = {
        position: [x, -y, this.depth + 0.7],
        name: data.name,
        value: data.value,
        level: data.level,
        index: index,
      };
      this.InfoPointGroup.add(sprite);
      // 标签
      let label = infoLabel(data, label3d, this.InfoPointGroup);
      this.infoLabelElement.push(label);

      // 事件
      this.interactionManager.add(sprite);
      sprite.addEventListener("mousedown", (ev) => {
        if (this.clicked) return false;
        this.clicked = true;
        this.infoPointIndex = ev.target.userData.index;
        this.infoLabelElement.map((label) => {
          label.hide();
        });
        label.show();
        this.createInfoPointLabelLoop();
      });
      sprite.addEventListener("mouseup", (ev) => {
        this.clicked = false;
      });
      sprite.addEventListener("mouseover", (event) => {
        document.body.style.cursor = "pointer";
      });
      sprite.addEventListener("mouseout", (event) => {
        document.body.style.cursor = "default";
      });
    });

    function infoLabel(data, label3d, labelGroup) {
      let label = label3d.create("", "info-point", true);
      const [x, y] = self.geoProjection([data.lng, data.lat]);
      label.init(
        ` <div class="info-point-wrap">
        <div class="info-point-wrap-inner">
          <div class="info-point-line">
            <div class="line"></div>
            <div class="line"></div>
            <div class="line"></div>
          </div>
          <div class="info-point-content">
            <div class="content-item"><span class="label">名称</span><span class="value">${data.name}</span></div>
            <div class="content-item"><span class="label">PM2.5</span><span class="value">${data.value}ug/m²</span></div>
            <div class="content-item"><span class="label">等级</span><span class="value">${data.level}</span></div>
          </div>
        </div>
      </div>
    `,
        new Vector3(x, -y, 2.4)
      );
      label3d.setLabelStyle(label, 0.015, "x");
      label.setParent(labelGroup);
      label.hide();
      return label;
    }
  }
  // 标签循环显示
  createInfoPointLabelLoop() {
    clearInterval(this.infoPointLabelTime);
    this.infoPointLabelTime = setInterval(() => {
      this.infoPointIndex++;
      if (this.infoPointIndex >= this.infoLabelElement.length) {
        this.infoPointIndex = 0;
      }
      this.infoLabelElement.map((label, i) => {
        if (this.infoPointIndex === i) {
          label.show();
        } else {
          label.hide();
        }
      });
    }, 3000);
  }
  // 转换坐标
  geoProjection(args) {
    return geoMercator().center(this.pointCenter).scale(120).translate([0, 0])(
      args
    );
  }
  // 更新
  update() {
    super.update();
    this.interactionManager && this.interactionManager.update();
  }
  // 销毁
  destroy() {
    super.destroy();
    this.label3d && this.label3d.destroy();
  }
}
